/**
 * @file logger.h
 * @brief
 * @author Wang Bo
 * @version 1.0
 * @date 2021-03-22
 *
 * @copyright  co.,Ltd.
 *
 * @par 修改日志:
 * 	Date	Version	Author
 * 	2021-03-22	1.0	Wang Bo
 * 	内容：
 * 	利用临时对象，构造时在对象内的缓冲区构造日志内容，析构时将缓冲区内容写入文件或输出到屏幕
 */
#ifndef __LOGGER_H__
#define __LOGGER_H__

#ifndef NS_WB
#define NS_WB
#define NS_WB_BEGIN namespace wb {
#define NS_WB_END }
#endif

#include <sstream>
#include <mutex>
#include <string.h>
#include <datetime.h>
NS_WB_BEGIN //begin namespace wb


enum LogLevel
{
    TRACE, DEBUG, INFO,
    WARN, ERROR, FATAL, MAXLEVEL
};

class Logger
{
    //编译时取得不带路径的文件名basename，减少运行时的运算
    class SourceFile
    {
      public:
        template<int N>
        inline SourceFile(const char (&arr)[N]) //对于数组型的入参的模板构造函数
            : data_(arr)
            , size_(N - 1)
        {
            const char* slash = strrchr(data_, '/'); //找到倒数第一个'/'，截取字符串
            if (slash != nullptr)
            {
                data_ = slash + 1; //如果找到'/'，从'/'+1的位置开始截取字符串作为文件名
                size_ = data_ - arr;
            }
        }

        explicit SourceFile(const char* filename) //对于字符串常量的入参构造函数，显式调用
            : data_(filename)
        {
            const char* slash = strrchr(data_, '/');
            if (slash != nullptr)
            {
                data_ = slash + 1;
            }
            size_ = strlen(data_);
        }

        const char* data_; //文件名字符串常量
        size_t size_; //文件名字符串长度
    };

  public:
    Logger(SourceFile file, int line, LogLevel lvl);
    ~Logger();
    std::ostringstream& stream() { return oss_; } //返回一个oss的引用
    static LogLevel logLevel(); //获取当前日志级别
    static void setLogLevel(LogLevel lvl); //设置日志级别，低于该级别的日志不会被显示


  protected:
    SourceFile filename_; //文件名字符串常量
    const int lineno_; //行号常量
    const Date date_; //日期时间类型常量
    LogLevel level_; //当前临时对象构造时日志级别，用于取lvlstr数组下标构造日志字符串中的级别字符串
    static LogLevel gs_level; //静态的日志级别
    static std::mutex logmtx_;

  private:
    std::ostringstream oss_; //使用STL的stringstream作为对象内部缓冲区
};


NS_WB_END //end of namespace wb

// 定义一些日志宏

/**
 * @brief 日志宏内部实现，传入文件名，行号，日志级别，构造Logger类临时对象，
 * 并且调用stream()获得std::ostringstream对象引用
 */
#define __LOG(L)\
wb::Logger(__FILE__, __LINE__, L).stream()

/**
 * @brief 宏参数传入日志级别，不低于日志类静态级别的才可以调用__LOG(L)宏输出
 */
#define LOG(L)\
if (L >= wb::Logger::logLevel())\
    __LOG(L)

/**
 * @brief 当满足cond时才输出ERROR级别日志
 * 一般是用来判断某些条件，如果不符合预期，ERROR级别比较合理
 */
#define LOG_IF(cond)\
if ((cond))\
    __LOG(wb::LogLevel::ERROR)


#endif // __LOGGER_H__